#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>

#define COMDSIZE 1024
#define ARGVSIZE 100
#define EXITCODE 40

char command[COMDSIZE];// 直接获取命令
char* argv[ARGVSIZE];// 获取命令子串
char pwd[COMDSIZE];// 当前路径
char hostname[COMDSIZE];// 主机名
char myenv[COMDSIZE];// 存放导入的环境变量
int quit;// main内循环的条件
int lastcode;// 退出码

char* getur(){
	return getenv("USER");
}

void gethname(){
	gethostname(hostname,sizeof(hostname));
}	

void getpd(){
	getcwd(pwd,sizeof(pwd));
}

int interact()
{
	getpd();// 刷新pwd
	gethname();// 刷新hostname
	const char* _user = getur();// 获取用户名

	if(strcmp(_user,"root") == 0){// root用户	
		printf("[%s@%s:%s]# ",_user,hostname,pwd);		
	}
	else{// 普通用户
		printf("[%s@%s:%s]$ ",_user,hostname,pwd);		
	}
	
	char* s = fgets(command,sizeof(command),stdin);// 从输入流上获取指令
    if(!s)assert("fgets");// 获取失败直接报错
	(void)s;// s没用了

	command[strlen(command)-1] = '\0';// fgets会拿到\n 这里要把\n删除
}

int splitstring()
{
	int i = 0;
	argv[i] = strtok(command," ");
	if(argv[i] == NULL)return 0;// 命令行没有输入指令

	while(argv[++i] = strtok(NULL," ")){;}// 分割完存到argv
	return i;
}

int buildCommand(int argc)
{
	// 处理内建命令 export echo cd 
	if(argc >= 2 && strcmp(argv[0],"export") == 0)
	{
		strcpy(myenv,argv[1]);
		putenv(myenv);
		return 0;
	}
	else if(argc >= 2 && strcmp(argv[0],"echo") == 0)
	{
		if(strcmp(argv[1],"$?") == 0)// 处理打印退出码
		{
			printf("%d\n",lastcode);
			lastcode=0;
		}
		else if(argv[1][0] == '$' && strlen(argv[1]) != 1)// 处理打印环境变量
		{
			// echo $USER
			printf("%s\n",getenv(argv[1]+1));// 获取$后的内容
		}
		else// 其余情况直接打印
		{
			printf("%s\n",argv[1]);
		}

		return 0;
	}
	else if(argc >= 2 && strcmp(argv[0],"cd") == 0)
	{
		chdir(argv[1]);// 改变当前路径
		getpd();// 刷新全局变量pwd
		sprintf(getenv("PWD"),"%s",pwd);// 修变环境变量PWD
		return 0;
	}

	return -1;// 不是内建命令
}

void NormalExcute()
{
	pid_t id = fork();

	if(id == 0)//子
	{
		execvp(argv[0],argv);// 进程替换 execlp("ls","ls","-l","-a",NULL)
		// 若走到这里，说明进程替换失败，子进程需要编写特殊的退出码
		exit(EXITCODE);
	}
	else if(id > 0)// 父
	{
		// 等待子进程 + 获取子进的信息
		int status = 0;
		pid_t ret = waitpid(id,&status,0);

		if(ret == id)// 等待成功
		{
			lastcode = status&0x7F;// 获取进程的退出码
		}
	}
	else// 发生错误
	{
		perror("fork");
	}
}

int main()
{
	while(!quit)
	{
		// 1.完成交互，获取命令
		interact();	
	   
		// 2.分割指令
		int ret = splitstring();
		if(ret == 0)continue;
	  
		// 3.判断指令
		int n=buildCommand(ret);

		// 4.普通指令
		if(n)NormalExcute();
   }
	
	return 0;
}
